#ifndef VECTOR_H
#define VECTOR_H

//-----------------------------------------------------------------
// Includes
//-----------------------------------------------------------------
#include <iostream.h>
#include <math.h>

namespace Viewer
{
	//-----------------------------------------------------------------
	//	This class defines a three-dimensional vector, with appropriate
	//	operators.  (* is cross product.)
	//-----------------------------------------------------------------
	class Vector {
	   public:
		float x,y,z;				// vector components
		
		Vector() { x=y=z=0; }			// constructor
		Vector(const float pX, const float pY, const float pZ)
		: x(pX), y(pY), z(pZ) {}

		// vector arithmatic

		Vector operator+(const Vector & pV) const
		{ Vector out( x+pV.x, y+pV.y, z+pV.z ); return out; }

		Vector operator-(const Vector & pV) const
		{ Vector out( x-pV.x, y-pV.y, z-pV.z ); return out; }

		Vector& operator+=(const Vector & pV)
		{ x+=pV.x; y+=pV.y; z+=pV.z; return *this; }

		Vector& operator-=(const Vector & pV)
		{ x-=pV.x; y-=pV.y; z-=pV.z; return *this; }

		float Dot(const Vector & pV) const			// dot product
		{ return x*pV.x + y*pV.y + z*pV.z; }

		Vector operator*(const Vector & pV) const		// cross product
		{ Vector out( y*pV.z-z*pV.y, z*pV.x-x*pV.z, x*pV.y-y*pV.x ); 
		  return out; }

		Vector Cross( const Vector & pV ) const
		{ return *this * pV; }

		friend float Dot( const Vector & p1, const Vector & p2 )
		{ return p1.Dot(p2); }

		friend Vector Cross( const Vector & p1, const Vector & p2 )
		{ return p1 * p2; }

		Vector operator*(const float pR) const		// * a scalar
		{ Vector out( x*pR, y*pR, z*pR ); return out; }

		friend Vector operator*(const float pR, const Vector & pV)
		{ Vector out( pV.x*pR, pV.y*pR, pV.z*pR ); return out; }

		Vector& operator*=(const float pR)
		{ x*=pR; y*=pR; z*=pR; return *this; }

		// Norme
		void Normalize(void)
		{
			float length = Length();
			x = x / length;
			y = y / length;
			z = z / length;
		}

		// magnitude
		float LenSqr(void) const
		{ return x*x+y*y+z*z; }		

		float Length(void) const
		{ return (float)sqrt(LenSqr()); }

		// projection (component of *this parallel to pV)
		// Note: component perpendicular to pV is:  *this - Proj(pV)

		Vector Proj(const Vector & pV)
		{ Vector out( pV * (this->Dot(pV) / pV.LenSqr()) ); return out; }

		// cosine of the angle between two vectors:
		float CosAng( const Vector & pV)
		{ return this->Dot(pV) / (Length() * pV.Length()); }

		// comparison
		int operator==( const Vector & pV) const
		{ return x==pV.x && y==pV.y && z==pV.z; }

		int operator!=( const Vector & pV) const
		{ return x!=pV.x || y!=pV.y || z!=pV.z; }

		// barycentre
		Vector Barycentre2D( const Vector & v1, const Vector & v2 )
		{
			Vector bary( 1/3*(x + v1.x + v2.x), 1/3*(y + v1.y + v2.y), 1/3*(z + v1.z + v2.z) );			
			return bary;
		}

		Vector Barycentre3D( const Vector & v1, const Vector & v2, const Vector & v3 )
		{
			Vector bary( 1/4*(x + v1.x + v2.x + v3.x), 1/4*(y + v1.y + v2.y + v3.y), 1/4*(z + v1.z + v2.z + v3.z) );			
			return bary;
		}

		// input/output
		friend ostream& operator<<(ostream& pStr, const Vector& pV)
		{ return (pStr << '(' << pV.x << ',' << pV.y << ',' << pV.z << ')'); }

		friend istream& operator>>(istream pStr, Vector& pV)
		{ return (pStr >> pV.x >> pV.y >> pV.z); }
	};
}

#endif
